package server;

import java.rmi.AlreadyBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.Registry;
import java.rmi.server.ServerNotActiveException;
import java.rmi.server.UnicastRemoteObject;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import common.IServer;
import common.IUI;
import common.Profile;

public class Server implements IServer {

    private List<Room> waitingRooms;
    private Map<Profile, IUI> clients;
    
    private DBLogic dbl;
    
    private static final int PORT = 40756;
    private static Registry registry;
     
    public Server() {
        waitingRooms = new ArrayList<Room>();
        clients = new HashMap<Profile, IUI>();
        dbl = new DBLogic();
        dbl.InitDBConnection();
    }
    
    protected void finalize()
    {
    	dbl.ShutDBConnection();
    }
    
    public static void main(String[] args) {
       
        try {        	
            registry = java.rmi.registry.LocateRegistry.createRegistry(PORT); 
            Server server = new Server();
            IServer remoteServer = (IServer) UnicastRemoteObject.exportObject(server, PORT);

            registry.bind("Server", remoteServer);
            
            System.out.println("Server connected.");
            

        } catch (RemoteException re) {
            System.err.println("Connection problem");
        } catch (AlreadyBoundException e) {
            /* this can't really happen */
        }
        
        
        while( true ) {
            try {
                Thread.sleep(1000 * 100);
            } catch (InterruptedException e) {            	
                e.printStackTrace();
            }
        }
        
    }
    
    // TODO stub method for testing purposes
    public List<Room> getRooms() {
        return waitingRooms;
    }
    
    
    private void createRoom(Profile profile, IUI playerUI) {
        
        int roomSize = 0;
        try {
            roomSize = playerUI.getNumberOfPlayers();
        } catch (RemoteException e) {
            e.printStackTrace();
        }
        
        Room room = new Room(profile, playerUI, roomSize);
        waitingRooms.add(room);
    }
    
    private int chooseRoom(IUI playerUI) throws RemoteException {
    	playerUI.printMessage(26);
    	for (int i = 1;i<=waitingRooms.size();i++) {
    		playerUI.printMessage("Room number: "+i+"  -  " + waitingRooms.get(i-1).getNumOfPlayersInRoom()+"/"+waitingRooms.get(i-1).getRoomSize());
    		for (Profile profile: waitingRooms.get(i-1).getClients().keySet()) {
    			if (profile.isGuest()) {
    				playerUI.printMessage(profile.getUserName());
    			} else {
    				playerUI.printMessage(profile.getUserName()+" - "+profile.getRating());
    			}
    		}
    		playerUI.printMessage("");
    	}
    	return (playerUI.getUserRoomDecision(waitingRooms.size())-1);
    }
       
    private void joinRoom(Profile profile, IUI playerUI) {
        if (waitingRooms.size() == 0) {
            try {
                playerUI.printMessage(24);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            createRoom(profile, playerUI);
        } else {
        	int roomChoice = 0;
			try {
				roomChoice = chooseRoom(playerUI);
			} catch (RemoteException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
            boolean goodUsername = waitingRooms.get(roomChoice).addToRoom(profile, playerUI);
            if (!goodUsername) {
                try {
                    playerUI.printMessage("You are already inside the room.");
                    playerUI.closeExportedObject();
                    return;
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
            
            if (waitingRooms.get(roomChoice).isFull())
            {
                List<Profile> profilesList = new ArrayList<Profile>(clients.keySet());
                List<IUI> UIList = new ArrayList<IUI>(clients.values());
                
                profiledGame NewGame = new profiledGame(profilesList, UIList, dbl);

                (new Thread(NewGame)).start();
                
                for (Profile playerProfile : waitingRooms.get(roomChoice).getClients().keySet())
                {
                    clients.remove(playerProfile);
                }
                
                waitingRooms.remove(roomChoice);                
            }
        }
    }
    
    
    public synchronized boolean joinServer(IUI playerUI) throws RemoteException {

        Profile userProfile = null;
        
        int loginDecision = playerUI.getUserLoginDecision();
        
        switch (loginDecision) {
        case 1:
            {
                Profile dummyProfile = null;
                boolean userExists = false;
                do {    
                    if (userExists) {
                        playerUI.printMessage("Chosen username already exists, " +
                                "choose a different name");
                    }
                    
                    userExists = false;
                    
                    userProfile = playerUI.getRegistrationDetails();
                    
                    try {
                        dummyProfile = dbl.queryProfile(userProfile.getUserName());
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                    
                    if ( dummyProfile != null ) {
                        userExists = true;
                    } 
                    
                } while (userExists);
                
                dbl.addNewProfile(userProfile);
                break;
            }
        case 2:
            {
                int tries = 0;              
                while (tries < 3)
                {
                    String[] arrUserAndPass = playerUI.getLoginDetails(tries);
                   
                    Profile tempProfile = null;
                    try {
                        tempProfile = dbl.queryProfile(arrUserAndPass[0]);
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                    
                    if (tempProfile != null && tempProfile.getPassword().equals(arrUserAndPass[1]))
                    {
                        userProfile = tempProfile;
                        break;                    
                    }
                    tries += 1;
                }
                
                if (userProfile == null)
                {
                    playerUI.printMessage("Exceeded allowed number of attempts. Disconnecting.");
                    playerUI.closeExportedObject();
                    return false;
                }
                else
                {
                    playerUI.printMessage("Logged in succesfully");
                }
                break;
            }
        case 3:
            {
                String guestName;
                int num = 0; 
                boolean nameTaken;
                do
                {              
                    guestName = "guest";
                    nameTaken = false;
                    guestName += num;                    
                    num++;
                    
                    for (Profile curProf : clients.keySet())
                    {
                        if (curProf.getUserName().equals(guestName))
                        {
                            nameTaken = true;
                            break;
                        }
                    }
                }
                while (nameTaken);

                userProfile = new Profile(guestName);
                
                break;
            }
            
        }
        
        int decision;
        
        clients.put(userProfile, playerUI);
        decision = playerUI.getUserSetupDecision(userProfile.isGuest());
        while (decision==4) {
        	if (profileOptions(userProfile,playerUI)) {
        		dbl.updateProfile(userProfile);
        	}
        	decision = playerUI.getUserSetupDecision(userProfile.isGuest());
        }
        switch (decision) {
        case 1:
            createRoom(userProfile, playerUI);
            break;
        case 2:
            joinRoom(userProfile, playerUI);
            break;
        case 3:
            playAgainstComputer(userProfile, playerUI);
            break;
        case 5:
            playerUI.printMessage(38);
            playerUI.closeExportedObject();
            return true;
        }  
        
        return true;            

    }
    
    private boolean profileOptions(Profile profile, IUI playerUI) throws RemoteException {
    	int decision;
    	boolean changeMade=false,temp;
    	while (true) {
    		decision = playerUI.getUserProfileDecision();
            switch (decision) {
            case 1:
            	temp = changePassword(profile, playerUI);
            	if (!changeMade) {
            		changeMade = temp;
            	}
                break;
            case 2:
            	temp = changeEmail(profile, playerUI);
            	if (!changeMade) {
            		changeMade = temp;
            	}
                break;
            case 3:
            	playerUI.printHistory(profile.getHistory());
                break;
            case 4:
                playerUI.printMessage(36, profile.getRating());
                break;
            case 5:
                return changeMade;	
            }
    	}
    }
    
    private boolean changePassword(Profile profile, IUI playerUI) throws RemoteException {
    	String newPassword = playerUI.changePasswordDialog(profile.getPassword());
    	if (newPassword == null) return false;
    	profile.setPassword(newPassword);
    	playerUI.printMessage(37);
    	return true;
    }
    
    private boolean changeEmail(Profile profile, IUI playerUI) throws RemoteException {
		String newEmail = playerUI.changeEmailDialog();
		if (newEmail == null) return false;
		profile.setEmail(newEmail);
		playerUI.printMessage(37);
		return true;
    }
  
    private void playAgainstComputer(Profile playerProfile, IUI playerUI)
    {
        List<Profile> profilesList = new ArrayList<Profile>();
        List<IUI> UIList = new ArrayList<IUI>();
        
        profilesList.add(playerProfile);
        UIList.add(playerUI);
        
        CompPlayer computer = new CompPlayer();       
        profilesList.add(new Profile("Computer"));
        UIList.add(computer);
        
        profiledGame NewGame = new profiledGame(profilesList, UIList, dbl);
        
        (new Thread(NewGame)).start();
        
        clients.remove(playerProfile);
    }
    
    
    public synchronized String getClientIP() {
        try {
            return UnicastRemoteObject.getClientHost();
        } catch (ServerNotActiveException e) {
            return null;
        }
    }
    
    
    //pigumim for testing
    
    public Registry getRegistry()
    {
        return registry;
    }
    
    public void setRegistry(Registry reg)
    {
        registry = reg;
    }
    
    public int getPort()
    {
        return PORT;
    }
    
}